// IR.h
// This file contains the definition of intermediate representation
#ifndef IR_H
#define IR_H

#include "crossbit/common.h"
#include <iostream>
#include <vector>
#include <cmath>
#include <assert.h>
#include <stdarg.h>

namespace crossbit {

// -------------------------------------------------------------------
//	    Shortcuts for creating virtual instructions
// -------------------------------------------------------------------

//#define PUT(a, b)		(VInst2::createPUT((a), (b)))
//#define GET(a, b)		(VInst2::createGET((a), (b)))
#define PUT(a, b, c)		(VInst2::createPUT((a), (b), (c)))
#define GET(a, b, c)		(VInst2::createGET((a), (b), (c)))

#define LD(a, b, c, d)		(VInst3::createLD((a), (b), (c), (d)))
#define ST(a, b, c, d)		(VInst3::createST((a), (b), (c), (d)))

#define USE(a)			(VInst1::createUSE(a))
#define LABEL(a)		(VInst1::createLABEL(a))
#define LOOP(a, b, c)		(VInst3::createLOOP((a), (b), (c)))
#define LOOPN(a, b, c)		(VInst3::createLOOPN((a), (b), (c)))
#define MOV(a, b, c)		(VInst2::createMOV((a), (b), (c)))
#define MOV_T_R(a, b, c)		(VInst2::createMOV_T_R((a), (b), (c)))
#define MOV_R_T(a, b, c)		(VInst2::createMOV_R_T((a), (b), (c)))
#define LI(a, b, c)		(VInst2::createLI((a), (b), (c)))
#define BSF(a, b)		(VInst2::createBSF((a), (b)))
#define BSR(a, b)		(VInst2::createBSR((a), (b)))

#define SEXT(a, b, c)   	(VInst2::createSEXT((a), (b), (c)))
#define ZEXT(a, b, c)   	(VInst2::createZEXT((a), (b), (c)))
#define ADD(a, b, c, d)		(VInst3::createADD((a), (b), (c), (d)))
#define ADDI(a, b, c, d)	(VInst3::createADDI((a), (b), (c), (d)))
#define SUB(a, b, c, d)		(VInst3::createSUB((a), (b), (c), (d)))
#define SUBI(a, b, c, d)	(VInst3::createSUBI((a), (b), (c), (d)))
#define AND(a, b, c, d)		(VInst3::createAND((a), (b), (c), (d)))
#define OR(a, b, c, d)		(VInst3::createOR((a), (b), (c), (d)))
#define XOR(a, b, c, d)		(VInst3::createXOR((a), (b), (c), (d)))
#define NOT(a, b, c)		(VInst2::createNOT((a), (b), (c)))
#define MUL(a, b, c, d, e, f)	(VInst5::createMUL((a), (b), (c), (d), (e), (f)))
//#define MULU(a, b, c, d)	(VInst4::createMULU((a), (b), (c), (d)))
#define DIV(a, b, c, d, e, f)	(VInst5::createDIV((a), (b), (c), (d), (e), (f)))
//#define DIVU(a, b, c, d)	(VInst4::createDIVU((a), (b), (c), (d)))
#define SLL(a, b, c, d)		(VInst3::createSLL((a), (b), (c), (d)))
#define SRL(a, b, c, d)		(VInst3::createSRL((a), (b), (c), (d)))
#define SRA(a, b, c, d)		(VInst3::createSRA((a), (b), (c), (d)))
//#define SLLV(a, b, c)		(VInst3::createSLLV((a), (b), (c)))
//#define SRLV(a, b, c)		(VInst3::createSRLV((a), (b), (c)))
//#define SRAV(a, b, c)		(VInst3::createSRAV((a), (b), (c)))
#define CMP(a, b, c, d)		(VInst4::createCMP((a), (b), (c), (d)))

#define JMP(a, b)		(VInst2::createJMP((a), (b)))
#define BRANCH(a, b, c, d, e)	(VInst5::createBRANCH((a), (b), (c), (d), (e)))
//#define DIVU64(a, b, c, d, e)   (VInst5::createDIVU64((a), (b), (c), (d), (e)))

#define HALT(a)			(VInst0::createHALT(a))
#define SYSCALL			(VInst0::createSYSCALL())
#define CALL(a, b, ...)		(VInstVar::createCALL((a),(b),  __VA_ARGS__))

// Float Point Instructions

#define FPUTS(a)		(VInst1::createFPUTS((a)))
//#define FPUTD(a)		(VInst1::createFPUTD((a)))
#define FGETS(a)		(VInst1::createFGETS((a)))
//#define FGETD(a)		(VInst1::createFGETD((a)))

#define FLD(a,b,c,d)		(VInst3::createFLD((a),(b),(c),(d)))
#define FST(a,b,c,d)		(VInst3::createFST((a),(b),(c),(d)))

#define FMOV(a,b)		(VInst2::createFMOV((a),(b)))
#define FCOS(a)  		(VInst2::createFCOS((a)))
#define FPTAN(a)  		(VInst2::createFPTAN((a)))
#define FSIN(a)  		(VInst2::createFSIN((a)))
#define FCHS()  		(VInst0::createFCHS())
#define FSCALE()  		(VInst0::createFSCALE())
#define F2XM1()  		(VInst0::createF2XM1())
#define FYL2XP1()  		(VInst0::createFYL2XP1())
#define FYL2X()  		(VInst0::createFYL2X())
#define FLDL2E()  		(VInst0::createFLDL2E())
#define FLDLG2()  		(VInst0::createFLDLG2())
#define FLDLN2()  		(VInst0::createFLDLN2())
#define FXAM()  		(VInst0::createFXAM())
#define FRNDINT()		(VInst0::createFRNDINT())
#define FADDS()			(VInst0::createFADDS())
#define FADDD()			(VInst0::createFADDD())
#define FSUBS()			(VInst0::createFSUBS())
#define FSUBD()			(VInst0::createFSUBD())
#define FMULS()			(VInst0::createFMULS())
#define FMULD()			(VInst0::createFMULD())
#define FDIVS()			(VInst0::createFDIVS())
#define FDIVD()			(VInst0::createFDIVD())
#define FNEGS()			(VInst0::createFNEGS())
#define FNEGD()			(VInst0::createFNEGD())
#define FABSS()			(VInst0::createFABSS())
#define FABSD()			(VInst0::createFABSD())
#define FSQRTS()		(VInst0::createFSQRTS())
#define FSQRTD()		(VInst0::createFSQRTD())
#define FCMPS(a,b,c,d)		(VInst4::createFCMPS((a),(b),(c),(d)))
#define FCMPD(a,b,c,d)		(VInst4::createFCMPD((a),(b),(c),(d)))

#define CONV(a,b,c,d)		(VInst2::createCONV((a),(b),(c),(d)))
#define FINCSTP()		(VInst0::createFINCSTP())
// ---------------------------------------------------------------------


    // Code Addressing Mode
    enum PCType {
	SPC,	    // Source PC
	TPC	    // Target PC
    };


    // Compare Condition Code
    enum CmpCC {
	EQ,	// equal
	NE,	// not equal
	GT,	// greater than
	GE,	// greater than or equal
	LT,	// less than
	LE,	// less than or equal
	AB,	// above (unsigned GT)
	BE,	// below (unsigned LT)
    NA,	// not above
    };

    // Operand data type. Added by zhangyichao
    enum DataType
    {
        notype,
        b1,     // 1 bit
        b8,     // 8 bits
        b16,    // 16 bits
        b32,    // 32 bits
        b64,    // 64 bits
        u8,     // unsigned 1 byte
        u16,    // unsigned half word
        u32,    // unsigned word
        u64,    // unsigned dword
        s8,     // signed byte
        s16,    // signed half word
        s32,    // signed word
        s64,    // signed dword
        f16,    // 16-bit floating
        f32,    // 32-bit floating
        f64,    // 64-bit floating
    };

	// Tags for G-crossbit VBlockFilter
	enum OPR_TAG
	{
		TidX,
		TidY,
		TidZ,
		TidA,
		MatrixA,
		MatrixB,
		MatrixC,
		WA,
		WB,
		HA,
		HB,
		TEMP0,
		TEMP1,
		TEMP2,
		TEMP3,
		TEMP4,
		TEMP5,
		TEMP6,
		TEMP7,
		TEMP8,
		TEMP9,
		ARG0,
		ARG1,
		ARG2,
	};

    // VInstruction Operand
    typedef struct vinst_operand_t {
	enum operand_type {
	    SREG_USE,	// source register that is used
	    SREG_DEF,	// source register that is defined
	    VREG_USE,   // virtual register that is used
	    VREG_DEF,	// virtual register that is defined
	    FREG_USE,	// float-point virtual register that is used
	    FREG_DEF,	// float-point virtual register that is defined
	    IMM,
	    SIZE,
	    CC,
	    WORD,
		TAG,		// added by zhangyichao, for LDFilter of G-crossbit only
	    INVALID,
	} type;

	union {
	    // register number (including SREG_USE, SREG_DEF, VREG_USE, VREG_DEF, FREG)
	    RegNum reg;

	    /*
	    // data address specified by disp(reg)
	    struct {
		RegNum  reg;
		Word    disp;
	    } mem;

	    // program counter
	    struct {
		PCType type;    // Source PC or Target PC ?
		RegNum reg;
		Word   disp;
	    } pc;
	    */

	    // immediate value
	    Word imm;

	    // BYTE / HALFWORD / WORD 
	    BlockSize size;

	    // compare condition
	    CmpCC	cc;

		// Tags for Matrix_mul
		OPR_TAG tag;

	    // just plain word
	    Word word;
	};

	vinst_operand_t(enum operand_type t)
	    : type(t)
	{
	}

	// Dummy anonymous constructor for initialize 
	// operand array
	vinst_operand_t()
	    :type(INVALID)
	{
	}
    
    } Operand;

	/*
	// ignore USE & DEF
	inline bool operator==(const Operand &lopr, const Operand &ropr)
	{
		return ( (lopr.word == ropr.word) && ((lopr.type == Operand::SREG_USE && ropr.type == Operand::SREG_DEF) || (lopr.type == Operand::SREG_DEF && ropr.type == Operand::SREG_USE) || (lopr.type == Operand::VREG_USE && ropr.type == Operand::VREG_DEF) || (lopr.type == Operand::VREG_DEF && ropr.type == Operand::VREG_USE) || (lopr.type == Operand::FREG_USE && ropr.type == Operand::FREG_DEF) || (lopr.type == Operand::FREG_DEF && ropr.type == Operand::FREG_USE)) );
		//return ((abs(lopr.type - ropr.type) <= 1) && (lopr.word == ropr.word));
	}

	inline bool isSameOpr(const Operand lopr, const Operand ropr)
	{
		return ( (lopr.word == ropr.word) && ((lopr.type == Operand::SREG_USE && ropr.type == Operand::SREG_DEF) || (lopr.type == Operand::SREG_DEF && ropr.type == Operand::SREG_USE) || (lopr.type == Operand::VREG_USE && ropr.type == Operand::VREG_DEF) || (lopr.type == Operand::VREG_DEF && ropr.type == Operand::VREG_USE) || (lopr.type == Operand::FREG_USE && ropr.type == Operand::FREG_DEF) || (lopr.type == Operand::FREG_DEF && ropr.type == Operand::FREG_USE)) );
	}
	*/

    enum Opcode {
		OP_INT_BASE = 0x0, // Token to indicate int inst begin
	OP_GET,  // 0	
    OP_PUT,  // 1
	OP_LD,   // 2
    OP_ST,   // 3
	OP_MOV,  // 4
    OP_LI,   // 5
	OP_LOOP, // 6
    OP_LABEL,  // 7
    OP_USE,    // 8
	OP_LOOPN,  // 9
	OP_SEXT,   // 10
    OP_ZEXT,   // 11
	OP_ADD,    // 12
    OP_SUB,	   // 13
	OP_ADDI,   // 14
    OP_SUBI,   // 15	
	OP_AND,    // 16
    OP_NOT,    // 17 
	OP_XOR,    // 18
    OP_OR,     // 19 
	OP_MUL,    // 20
    OP_MULU,   // 21 
	OP_DIV,    // 22
    OP_DIVU,   // 23 
	OP_SLL,    // 24
    OP_SRL,    // 25
    OP_SRA,    // 26 
	OP_SLLV,   // 27
    OP_SRLV,   // 28 
    OP_SRAV,   // 29
	OP_CMP,    // 30
	OP_JMP,    // 31
    OP_BRANCH, // 32
	OP_HALT,   // 33
    OP_SYSCALL,// 34
	OP_CALL,   // 35
	OP_INT_END, // Token to indicate int inst end
	//total 24 instructions

	//float point instructions
	OP_FLOAT_BASE, // Token to indicate float inst begins
	OP_FPUT_S, // 36
    OP_FGET_S, // 37
	OP_FPUT_D, // 38
    OP_FGET_D, // 39
	OP_FLD,	   // 40
    OP_FST,    // 41
	OP_FSCALE, // 42
    OP_FCHS,   // 43
    OP_FCOS,   // 44
    OP_FSIN,   // 45
    OP_FPTAN,  // 46
	OP_FYL2X,  // 47
    OP_FYL2XP1,// 48
    OP_F2XM1,  // 49
	OP_FMOV,   // 50
    OP_FLDLG2, // 51
    OP_FLDL2E, // 52
    OP_FLDLN2, // 53
	OP_FRNDINT, // 54
    OP_FXAM,    // 55
	OP_FADD_S,  // 56
    OP_FADD_D,  // 57
	OP_FSUB_S,  // 58
    OP_FSUB_D,  // 59
	OP_FMUL_S,  // 60
    OP_FMUL_D,  // 61
	OP_FDIV_S,  // 62
    OP_FDIV_D,  // 63
	OP_FNEG_S,  // 64
    OP_FNEG_D,  // 65
	OP_FABS_S,  // 66
    OP_FABS_D,  // 67
	OP_FSQRT_S, // 68
    OP_FSQRT_D, // 69
	OP_FCMP_S,  // 70
    OP_FCMP_D,  // 71
	OP_FLOAT_END, // Token to indicate float inst end
	
	OP_CONV,	// 72 //total 24 float-point v-instructions
	OP_FINCSTP,	// 73 //added by zhengjuyu
	OP_BSR,		// 74 //added by zhengjuyu
	OP_BSF,		// 75 //added by zhengjuyu
	OP_DIVU64,	// 76 //added by zhengjuyu
	OP_MOV_T_R,	// 77 //added by zhangyichao
	OP_MOV_R_T,	// 78 //added by zhangyichao

	OP_UNKNOWN,	// Opcode to be determined, added by zhangyichao
	OP_ERR,
	OP_ENTRY,
	OP_NEXT,
	};	    

	// Auxiliary functions for OpCode Judgement
	inline bool IsIntegerOp(const Opcode& op){
		return (op > OP_INT_BASE && op < OP_INT_END);
	}
	inline bool IsFloatOp(const Opcode& op){
		return (op > OP_FLOAT_BASE && op < OP_FLOAT_END);
	}

    enum Opkind {
	OPK_STATEMAP,		// map simulated machine state to/from virtual register
	OPK_MEMORYACCESS,	// load/store
	OPK_DATAMOVEMENT,	// register -> register, immediate -> register
	OPK_COMPUTATION,	// add, sub, and, not, xor, or, mul, div, shift, cmp ...
	OPK_CONTROLTRANSFER,	// jmp
	OPK_SPECIAL,		// any operation that doesn't fit into above categorie

	OPK_UNKNOWN,		// Opkind to be determined later, added by zhangyichao
    };

    // Virtual Instruction
    // base class of virtual instruction
    class VInst {
	// printing VInst for debugging purpose
	friend  std::ostream& operator<<(std::ostream&, const VInst&);

	public:
		VInst ()
			: op(OP_UNKNOWN), kind(OPK_UNKNOWN), dtype(notype), dtype2(notype), use_flags(false)
		{}

	    virtual ~VInst() 
	    {
	    }

	    inline Opcode getOpcode() const
	    {
		return op;
	    }

	    inline Opkind getOpkind() const
	    {
		return kind;
	    }

        inline DataType getDataType() const
        {
            return dtype;
        }
	    
        inline DataType getDataType2() const
        {
            return dtype2;
        }

	    inline MemAddr getVAddr() const
	    {
		return vaddr;
	    }
        inline void setUseFlags( bool flags ){
            use_flags = flags;
        }
        inline bool getUseFlags( void ){
            return use_flags;
        }

	    virtual Operand getOperand(int i) const = 0;
	    virtual void setOperand(int i, Operand oprnd) = 0;
	    virtual int getOperandNum() const = 0;
		virtual int getSize() = 0;

	protected:
	    VInst (Opcode opcode, Opkind opkind)
		: op(opcode), kind(opkind), dtype(s32), dtype2(s32), use_flags(false) 
		{
        }
	    VInst (Opcode opcode, Opkind opkind, MemAddr addr)
		: op(opcode), kind(opkind), dtype(s32), dtype2(s32), vaddr(addr), use_flags(false)
		{}

        // Added by zhangyichao
        VInst (Opcode opcode, Opkind opkind, DataType datatype)
            : op(opcode), kind(opkind), dtype(datatype), dtype2(s32), use_flags(false)
        {}

        VInst (Opcode opcode, Opkind opkind, DataType datatype1, DataType datatype2)
            : op(opcode), kind(opkind), dtype(datatype1), dtype2(datatype2), use_flags(false)
        {}

	private:
	    Opcode op;
	    Opkind kind;

        // added by zhangyichao
        DataType dtype;
        DataType dtype2;
	    
	    //added by Zheng Juyu
	    //The address of the source instruction
	    MemAddr vaddr;
        // added 2008-12-23
        bool use_flags;
    };
    
    // VInst with NO operands
    // Including instructions:
    class VInst0 : public VInst {
	public:
		VInst0 ()
			: VInst () {}
	    // Attention: undefined
	    Operand getOperand(int i) const 
	    { 
		assert(0 && "No Operand Available"); 
		return Operand(); // dummy, shut off warnings
	    }

	    void setOperand(int i, Operand oprnd)
	    {
		return;
	    }

	    int getOperandNum() const 
	    { 
		return 0; 
	    }

		int getSize()
		{
			return sizeof(VInst0);
		}
	    
	    static VInst *createHALT(MemAddr addr) 
	    {
		return new VInst0(OP_HALT, /*OPK_CONTROLTRANSFER*/OPK_SPECIAL, addr);
	    }

	    static VInst *createSYSCALL()
	    { 
		return new VInst0(OP_SYSCALL, OPK_SPECIAL);
	    }

	    static VInst* createFYL2XP1()
	    {
		return new VInst0(OP_FYL2XP1, OPK_COMPUTATION);
	    }

	    static VInst* createFYL2X()
	    {
		return new VInst0(OP_FYL2X, OPK_COMPUTATION);
	    }

	    static VInst* createFLDL2E()
	    {
		return new VInst0(OP_FLDL2E, OPK_COMPUTATION);
	    }

	    static VInst* createFLDLN2()
	    {
		return new VInst0(OP_FLDLN2, OPK_COMPUTATION);
	    }

	    static VInst* createFLDLG2()
	    {
		return new VInst0(OP_FLDLG2, OPK_COMPUTATION);
	    }
	    
	    /*static VInst* createFPTAN()
	    {
		return new VInst0(OP_FPTAN, OPK_COMPUTATION);
	    }

	    static VInst* createFSIN()
	    {
		return new VInst0(OP_FSIN, OPK_COMPUTATION);
	    }

	    static VInst* createFCOS()
	    {
		return new VInst0(OP_FCOS, OPK_COMPUTATION);
	    }*/

	    static VInst* createFCHS()
	    {
		return new VInst0(OP_FCHS, OPK_COMPUTATION);
	    }

	    static VInst* createFSCALE()
	    {
		return new VInst0(OP_FSCALE, OPK_COMPUTATION);
	    }

	    static VInst* createF2XM1()
	    {
		return new VInst0(OP_F2XM1, OPK_COMPUTATION);
	    }

	    static VInst* createFXAM()
	    {
		return new VInst0(OP_FXAM, OPK_COMPUTATION);
	    }
    
	    static VInst* createFRNDINT()
	    {
		return new VInst0(OP_FRNDINT, OPK_COMPUTATION);
	    }
	    
 	    // float-point arithmetic v-instructions
	    static VInst *createFADDS()
	    {
		return new VInst0(OP_FADD_S, OPK_COMPUTATION);
	    }
	    static VInst *createFADDD()
	    {
		return new VInst0(OP_FADD_D, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFSUBS()
	    {
		return new VInst0(OP_FSUB_S, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFSUBD()
	    {
		return new VInst0(OP_FSUB_D, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFMULS()
	    {
		return new VInst0(OP_FMUL_S, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFMULD()
	    {
		return new VInst0(OP_FMUL_D, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFDIVS()
	    {
		return new VInst0(OP_FDIV_S, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFDIVD()
	    {
		return new VInst0(OP_FDIV_D, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFNEGS()
	    {
		return new VInst0(OP_FNEG_S, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFNEGD()
	    {
		return new VInst0(OP_FNEG_D, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFABSS()
	    {
		return new VInst0(OP_FABS_S, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFABSD()
	    {
		return new VInst0(OP_FABS_D, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFSQRTS()
	    {
		return new VInst0(OP_FSQRT_S, OPK_COMPUTATION);
	    }
	  
	    static VInst *createFSQRTD()
	    {
		return new VInst0(OP_FSQRT_D, OPK_COMPUTATION);
	    }
	 
	   static VInst* createFINCSTP()
	   {
	       return new VInst0(OP_FINCSTP, OPK_COMPUTATION);
	   } 

	protected:
	    VInst0 (Opcode op, Opkind kind)
		: VInst (op, kind) {}
	    VInst0 (Opcode op, Opkind kind, MemAddr addr)
		: VInst (op, kind, addr) {}
    };

    // VInst with ONE operand
    class VInst1 : public VInst {
	public:
		VInst1 ()
			: VInst() {}
	    // Attention: only valide when i == 1, otherwise, undefined.
	    Operand getOperand(int i) const
	    {
		return operand;
	    }

	    void setOperand(int i, Operand oprnd)
	    {
		if (i == 1)
		    operand = oprnd;
	    }

	    int getOperandNum() const { return 1; }

		int getSize()
		{
			return sizeof(VInst1);
		}

	    static VInst *createLABEL(Word id)
	    {
		Operand opr1(Operand::IMM);
		opr1.imm = id;
		return new VInst1(OP_LABEL, OPK_COMPUTATION, opr1);
	    }

	    static VInst *createUSE(RegNum src)
	    {
		Operand opr1(Operand::VREG_USE);
		opr1.reg = src;
		return new VInst1(OP_USE, OPK_COMPUTATION, opr1);
	    }
   
            // r1: float point register number
	    static VInst *createFGETS(RegNum fpr1)
	    {
		Operand opr1(Operand::VREG_USE);
		opr1.reg = fpr1;
		return new VInst1(OP_FGET_S, OPK_STATEMAP, opr1);
	    }
	   
            // r1: float point register number
	   static VInst *createFPUTS(RegNum fpr1)
	   {
		Operand opr1(Operand::VREG_USE);
		opr1.reg = fpr1;
		return new VInst1(OP_FPUT_S, OPK_STATEMAP, opr1);
	   }

            // r1: float point register number
	    static VInst *createFGETD(RegNum fpr1)
	    {
		Operand opr1(Operand::VREG_USE);
		opr1.reg = fpr1;
		return new VInst1(OP_FGET_D, OPK_STATEMAP, opr1);
	    }
	   
            // r1: float point register number
	   static VInst *createFPUTD(RegNum fpr1)
	   {
		Operand opr1(Operand::VREG_USE);
		opr1.reg = fpr1;
		return new VInst1(OP_FPUT_D, OPK_STATEMAP, opr1);
	   }

	protected:
	    VInst1 (Opcode op, Opkind kind, Operand opr, DataType t = notype) 
		: VInst(op, kind, t), operand(opr) {
            if( (opr.type == Operand::VREG_USE || opr.type == Operand::VREG_DEF) && opr.reg >= 15 && opr.reg <= 18 ){

                this -> setUseFlags(true);
            }
        }

	private:
	    Operand operand;
    };

    // VInst with TWO operands
    class VInst2 : public VInst {
	public:
		VInst2 ()
			: VInst() {}
	    // Attention: only valid when i >= 1 && i <= 2, otherwise, undefined
	    Operand getOperand(int i) const 
	    {
		return operand[i - 1];
	    }

	    void setOperand(int i, Operand oprnd)
	    {
		operand[i - 1] = oprnd;
	    }

	    int getOperandNum() const { return 2; }

		int getSize()
		{
			return sizeof(VInst2);
		}
#if 0
	    // r1: source s-register number
	    // r2: destination v-register number
	    static VInst * createGET(RegNum r1, RegNum r2)
	    {
		Operand opr1(Operand::SREG), opr2(Operand::VREG_DEF);
		opr1.reg = r1;
		opr2.reg = r2;
		return new VInst2(OP_GET, OPK_STATEMAP, opr1, opr2);
	    }

	    // r1: source v-register number
	    // r2: destination s-register number
	    static VInst * createPUT(RegNum r1, RegNum r2)
	    {
		Operand opr1(Operand::VREG_USE), opr2(Operand::SREG);
		opr1.reg = r1;
		opr2.reg = r2;
		return new VInst2(OP_PUT, OPK_STATEMAP, opr1, opr2);
	    }
#endif	    
	    
		// trigonometric functions are added by zhangyichao
		// 2010-5-20
		static VInst *createFCOS(RegNum fpr1)
		{
			Operand opr1(Operand::FREG_USE),
					opr2(Operand::FREG_DEF);
			opr1.reg = fpr1;
			opr2.reg = fpr1;
			return new VInst2(OP_FCOS, OPK_COMPUTATION, opr1, opr2);
		}

		static VInst *createFSIN(RegNum fpr1)
		{
			Operand opr1(Operand::FREG_USE),
					opr2(Operand::FREG_DEF);
			opr1.reg = fpr1;
			opr2.reg = fpr1;
			return new VInst2(OP_FSIN, OPK_COMPUTATION, opr1, opr2);
		}
	
		static VInst *createFPTAN(RegNum fpr1)
		{
			Operand opr1(Operand::FREG_USE),
					opr2(Operand::FREG_DEF);
			opr1.reg = fpr1;
			opr2.reg = fpr1;
			return new VInst2(OP_FPTAN, OPK_COMPUTATION, opr1, opr2);
		}
	    // src: source float-point register number
	    // srcsize: source data size
	    // dst: destination float-point register number
	    // dstsize: destination data size
	    static VInst * createCONV(RegNum src, BlockSize srcsize, RegNum dst, BlockSize dstsize)
	    {
		Operand opr1(Operand::VREG_USE),
                opr2(Operand::VREG_USE); 
        DataType srctype, dsttype;

        switch(srcsize)
        {
            case HALFWORD:
                srctype = s16;
                break;
            case WORD:
                srctype = s32;
                break;
            case DWORD:
                srctype = s64;
                break;
            case FLOAT:
                srctype = f32;
                opr1.type = Operand::FREG_USE;
                break;
            case DOUBLE:
                srctype = f64;
                opr1.type = Operand::FREG_USE;
                break;
            default:
                assert(0 && "unimplemented srctype of CONV!\n");
        }

        switch(dstsize)
        {
            case WORD:
                dsttype = s32;
                break;
            case DWORD:
                dsttype = s64;
                break;
            case FLOAT:
                dsttype = f32;
                opr2.type = Operand::FREG_DEF;
                break;
            case DOUBLE:
                dsttype = f64;
                opr2.type = Operand::FREG_DEF;
                break;
            default:
                assert(0 && "unimplemented dsttype of CONV!\n");
        }

		opr1.reg = src;
		opr2.reg = dst;
		return new VInst2(OP_CONV, OPK_SPECIAL, opr1, opr2, srctype, dsttype);
	    }

	    // r1: source v-register number
	    // r2: destination v-register number
        // t : specifies which type will "r1" be extended to.
	    static VInst * createSEXT (RegNum r1, RegNum r2, DataType t)	
	    {
		Operand opr1(Operand::VREG_USE), 
			opr2(Operand::VREG_DEF);

		opr1.reg = r1;
		opr2.reg = r2;

		return new VInst2 (OP_SEXT, OPK_COMPUTATION, opr1, opr2, t);
	    }

	    // ditto
	    static VInst * createZEXT (RegNum r1, RegNum r2, DataType t)
	    {
		Operand opr1(Operand::VREG_USE), 
			opr2(Operand::VREG_DEF);

		opr1.reg = r1;
		opr2.reg = r2;

		return new VInst2 (OP_ZEXT, OPK_COMPUTATION, opr1, opr2, t);
	    }

	    // r1: source s-register number
	    // r2: destination v-register number
        // t : operands' data type
	    static VInst * createGET(RegNum r1, RegNum r2, DataType t)
	    {
		Operand opr1(Operand::SREG_USE), 
                opr2(Operand::VREG_DEF);

        // for FGETD
        if(t == f64)
		{
			opr1.type = Operand::FREG_USE;
            opr2.type = Operand::FREG_DEF;
		}

		opr1.reg = r1;
		opr2.reg = r2;

		return new VInst2(OP_GET, OPK_STATEMAP, opr1, opr2, t);
	    }

	    // r1: source v-register number
	    // r2: destination s-register number
        // t : operands' data type
	    static VInst * createPUT(RegNum r1, RegNum r2, DataType t)
	    {
		Operand opr1(Operand::VREG_USE), 
                opr2(Operand::SREG_DEF);

        // for FPUTD
        if(t == f64)
		{
            opr1.type = Operand::FREG_USE;
            opr2.type = Operand::FREG_DEF;
		}

		opr1.reg = r1;
		opr2.reg = r2;

		return new VInst2(OP_PUT, OPK_STATEMAP, opr1, opr2, t);
	    }

	    // r1: source v-register number
	    // r2: destination v-register number
        // t : operand's data type
	    static VInst * createNOT (RegNum r1, RegNum r2, DataType t)
	    {
		Operand opr1(Operand::VREG_USE), opr2(Operand::VREG_DEF);
		opr1.reg = r1;
		opr2.reg = r2;
		return new VInst2(OP_NOT, OPK_COMPUTATION, opr1, opr2, t);
	    }

	    // r1: source v-register number
	    // r2: destination v-register number
        // t : operands' data type
	    static VInst * createMOV (RegNum r1, RegNum r2, DataType t)
	    {
		Operand opr1(Operand::VREG_USE), opr2(Operand::VREG_DEF);
        DataType dtype;

		opr1.reg = r1;
		opr2.reg = r2;
        dtype = t;

		return new VInst2(OP_MOV, OPK_DATAMOVEMENT, opr1, opr2, dtype);
	    }

	    // tag: tag for PTX
	    // r  : destination v-register number
        // t  : operands' data type
	    static VInst * createMOV_T_R (OPR_TAG tag, RegNum r, DataType t)
	    {
		Operand opr1(Operand::TAG), opr2(Operand::VREG_DEF);

		if(t == f32 || t == f64)
			opr2.type = Operand::FREG_DEF;

		opr1.tag = tag;
		opr2.reg = r;

		return new VInst2(OP_MOV_T_R, OPK_DATAMOVEMENT, opr1, opr2, t);
	    }

	    // r  : source v-register number
	    // tag: tag for PTX
        // t  : operands' data type
	    static VInst * createMOV_R_T (RegNum r, OPR_TAG tag, DataType t)
	    {
		Operand opr1(Operand::VREG_USE), opr2(Operand::TAG);

		if(t == f32 || t == f64)
			opr1.type = Operand::FREG_USE;

		opr1.reg = r;
		opr2.tag = tag;

		return new VInst2(OP_MOV_R_T, OPK_DATAMOVEMENT, opr1, opr2, t);
		}
	    
	    // added by zhengjuyu
	    static VInst * createBSF(RegNum reg, RegNum dst)
	    {
		Operand opr1(Operand::VREG_USE),
			opr2(Operand::VREG_DEF);
		opr1.reg = reg;
		opr2.reg = dst;
		return new VInst2(OP_BSF, OPK_COMPUTATION, opr1, opr2);
	    }

	    // added by zhengjuyu
	    static VInst * createBSR(RegNum reg, RegNum dst)
	    {
		Operand opr1(Operand::VREG_USE),
			opr2(Operand::VREG_DEF);
		opr1.reg = reg;
		opr2.reg = dst;
		return new VInst2(OP_BSR, OPK_COMPUTATION, opr1, opr2);
	    }

	    // r1: source float-point register number
	    // r2: destination float-point register number
	    static VInst * createFMOV (RegNum r1, RegNum r2)
	    {
		Operand opr1(Operand::VREG_USE), opr2(Operand::VREG_USE);
		opr1.reg = r1;
		opr2.reg = r2;
		return new VInst2(OP_FMOV, OPK_DATAMOVEMENT, opr1, opr2);
	    }

	    // imm: source immediate value
	    // reg: destination v-register number
        // t  : operands' data type
	    static VInst * createLI (Word imm, RegNum reg, DataType t)
	    {
		Operand opr1(Operand::IMM), opr2(Operand::VREG_DEF);
        
		opr1.imm = imm;
		opr2.reg = reg;

		return new VInst2(OP_LI, OPK_DATAMOVEMENT, opr1, opr2, t);
	    }

	    // (disp)reg: jump destination, where "type" specifies SPC or TPC.
	    static VInst * createJMP(RegNum reg, Word disp)
	    {
		Operand opr1(Operand::VREG_USE);
		Operand opr2(Operand::IMM);
		opr1.reg = reg;
		opr2.imm = disp;
		return new VInst2(OP_JMP, OPK_CONTROLTRANSFER, opr1, opr2);
	    }
/*
	    static VInst *createFLD(RegNum reg, Word disp)
	    {
		Operand opr1(Operand::VREG_USE),
			opr2(Operand::IMM);

		opr1.reg = reg;
		opr2.imm = disp;
		return new VInst2(OP_FLD, OPK_DATAMOVEMENT, opr1, opr2);
	    }

	    static VInst *createFST(RegNum reg, Word disp)
	    {
		Operand opr1(Operand::VREG_USE),
			opr2(Operand::IMM);

		opr1.reg = reg;
		opr2.imm = disp;
		return new VInst2(OP_FST, OPK_DATAMOVEMENT, opr1, opr2);
	    }
*/
	protected:
	    VInst2 (Opcode op, Opkind kind, Operand opr1, Operand opr2, DataType t1 = notype, DataType t2 = notype)
		: VInst(op, kind, t1, t2)
	    {
		operand[0] = opr1;
		operand[1] = opr2;
        for( int i = 0 ; i < 2 ; i++ ) {
            if( (operand[i].type == Operand::VREG_USE || operand[i].type == Operand::VREG_DEF) && operand[i].reg >= 15 && operand[i].reg <= 18 ){
                this -> setUseFlags( true );
            }
        }

	    }

	private:
	    Operand operand[2];
    };
    
    // VInst with THREE operands
    class VInst3 : public VInst {
	public:
	    VInst3(Opcode op, Opkind kind, Operand opr1, Operand opr2, Operand opr3, DataType t = notype)
		: VInst(op, kind, t)
	    {
            operand[0] = opr1;
            operand[1] = opr2;
            operand[2] = opr3;
            for( int i = 0 ; i < 3 ; i++ ) {
                if( (operand[i].type == Operand::VREG_USE || operand[i].type == Operand::VREG_DEF) && operand[i].reg >= 15 && operand[i].reg <= 18 ){
                    this -> setUseFlags( true );
                    }
                }
        }
		VInst3()
			: VInst() {}
	    
	    // Attention: only valid when i >= 1 && i <= 3, otherwise, undefined
	    Operand getOperand(int i) const
	    {
		return operand[i - 1];
	    }

	    void setOperand(int i, Operand oprnd)
	    {
		operand[i - 1] = oprnd;
	    }
	    
	    int getOperandNum() const { return 3; }

		int getSize()
		{
			return sizeof(VInst3);
		}

	    // src: source float-point register number
	    // size: block size
	    // (disp)reg: memory address
	    static VInst * createFST(RegNum src, BlockSize size, RegNum reg, Word disp)
	    {
		Operand opr1(Operand::FREG_USE), 
			opr2(Operand::VREG_USE),
			opr3(Operand::IMM);
        DataType t;

        switch(size)
        {
            case WORD:
                {
                    t = f32;
                    break;
                }
            case DOUBLE:
                {
                    t = f64;
                    break;
                }
            default:
                {
                    assert(0 && "unimplemented datatype of createFST!");
                }
        }

		opr1.reg = src;
		opr2.reg = reg;
		opr3.imm = disp;
		return new VInst3(OP_ST, OPK_MEMORYACCESS, opr1, opr2, opr3, t);
	    }

	    // (disp)reg: memory address
	    // size: block size
	    // dst: destination v-register number
	    static VInst * createFLD(RegNum reg, Word disp, BlockSize size, RegNum dst)
	    {
		Operand opr1(Operand::VREG_USE),
			opr2(Operand::IMM),
			//opr3(Operand::VREG_USE);
			opr3(Operand::FREG_DEF);
        DataType t;

        switch(size)
        {
            case WORD:
                {
                    t = f32;
                    break;
                }
            case DWORD:
                {
                    t = s64;
                    break;
                }
            case DOUBLE:
                {
                    t = f64;
                    break;
                }
            default:
                {
                    assert(0 && "unimplemented size of FLD");
                }
        }

		opr1.reg = reg;
		opr2.imm = disp;
		opr3.reg = dst;
		return new VInst3(OP_LD, OPK_MEMORYACCESS, opr1, opr2, opr3, t);
	    }

	    // (disp)reg: memory address
	    // dst: destination v-register number
        // t: operands' data type
	    static VInst * createLD(RegNum reg, Word disp, RegNum dst, DataType t)
	    {
		Operand opr1(Operand::VREG_USE),
                opr2(Operand::IMM),
                opr3(Operand::VREG_DEF);
        DataType dtype;

		opr1.reg = reg;
		opr2.imm = disp;
		opr3.reg = dst;
        dtype = t;

		return new VInst3(OP_LD, OPK_MEMORYACCESS, opr1, opr2, opr3, dtype);
	    }

	    // src: source v-register number
	    // (disp)reg: memory address
        // t: operands' data type
	    static VInst * createST(RegNum src, RegNum reg, Word disp, DataType t)
	    {
		Operand opr1(Operand::VREG_USE), 
                opr2(Operand::VREG_USE),
                opr3(Operand::IMM);

		opr1.reg = src;
		opr2.reg = reg;
		opr3.imm = disp;
		return new VInst3(OP_ST, OPK_MEMORYACCESS, opr1, opr2, opr3, t);
	    }

	    // forward: 1 indicates jump forward, otherwise jump back
	    // id: not used now
	    // r: 0 indicates jump, otherwise no jump
	    static VInst * createLOOP(RegNum r, Word id, Word forward)
	    {
		Operand opr1(Operand::VREG_USE), opr2(Operand::IMM), opr3(Operand::IMM);
		opr1.reg = r;
		opr2.imm = id;
		opr3.imm = forward;
		return new VInst3(OP_LOOP, OPK_COMPUTATION, opr1, opr2, opr3);
	    }

	    // forward: 1 indicates jump forward, otherwise jump back
	    // id: not used now
	    // r: 1 indicates jump, otherwise no jump
	    static VInst * createLOOPN(RegNum r, Word id, Word forward)
	    {
		Operand opr1(Operand::VREG_USE), opr2(Operand::IMM), opr3(Operand::IMM);
		opr1.reg = r;
		opr2.imm = id;
		opr3.imm = forward;
		return new VInst3(OP_LOOPN, OPK_COMPUTATION, opr1, opr2, opr3);
	    }

#define CREATECOMP3(op)  \
	    static VInst *create##op (RegNum r1, RegNum r2, RegNum r3, DataType t)	\
	    {	\
		Operand opr1(Operand::VREG_USE),    \
			opr2(Operand::VREG_USE),    \
			opr3(Operand::VREG_DEF);    \
		opr1.reg = r1;		    \
		opr2.reg = r2;		    \
		opr3.reg = r3;		    \
		return new VInst3 (OP_##op, OPK_COMPUTATION, opr1, opr2, opr3, t);	\
	    }

	    //CREATECOMP3(ADD)
	    //CREATECOMP3(SUB)
	    CREATECOMP3(AND)
	    CREATECOMP3(XOR)
	    CREATECOMP3(OR)
#undef CREATECOMP3

		// Added by zhangyichao
		static VInst *createSUB(RegNum r1, RegNum r2, RegNum r3, DataType t)
		{
			Operand opr1(Operand::VREG_USE),
					opr2(Operand::VREG_USE),
					opr3(Operand::VREG_DEF);

			if(t == f64)
			{
				opr1.type = Operand::FREG_USE;
				opr2.type = Operand::FREG_USE;
				opr3.type = Operand::FREG_DEF;
			}

            opr1.reg = r1;
            opr2.reg = r2;
            opr3.reg = r3;

            return new VInst3(OP_SUB, OPK_COMPUTATION, opr1, opr2, opr3, t);
		}

        // Added by zhangyichao
        static VInst *createADD(RegNum r1, RegNum r2, RegNum r3, DataType t)
        {
            Operand opr1(Operand::VREG_USE),
                    opr2(Operand::VREG_USE),
                    opr3(Operand::VREG_DEF);

            if(t == f64)
            {
                opr1.type = Operand::FREG_USE;
                opr2.type = Operand::FREG_USE;
                opr3.type = Operand::FREG_DEF;
            }

            opr1.reg = r1;
            opr2.reg = r2;
            opr3.reg = r3;

            return new VInst3(OP_ADD, OPK_COMPUTATION, opr1, opr2, opr3, t);
        }

	    static VInst* createADDI(RegNum r1, Word imm, RegNum r2, DataType t)
	    {
		Operand opr1(Operand::VREG_USE),
			opr2(Operand::IMM),
			opr3(Operand::VREG_DEF);

		opr1.reg = r1;
		opr2.imm = imm;
		opr3.reg = r2;

		return new VInst3 (OP_ADDI, OPK_COMPUTATION, opr1, opr2, opr3, t);
	    }

	    static VInst* createSUBI(RegNum r1, Word imm, RegNum r2, DataType t)
	    {
		Operand opr1(Operand::VREG_USE),
			opr2(Operand::IMM),
			opr3(Operand::VREG_DEF);

		opr1.reg = r1;
		opr2.imm = imm;
		opr3.reg = r2;

		return new VInst3 (OP_SUBI, OPK_COMPUTATION, opr1, opr2, opr3, t);
	    }
	    
/*
#define CREATESHIFT(op)	\
	    static VInst *create##op (RegNum src, Byte n, RegNum dst, DataType t)	\
	    {	\
		Operand opr1(Operand::VREG_USE),    \
			opr2(Operand::IMM),	    \
			opr3(Operand::VREG_DEF);    \
		opr1.reg = src;		    \
		opr2.imm = n;		    \
		opr3.reg = dst;		    \
		return new VInst3 (OP_##op, OPK_COMPUTATION, opr1, opr2, opr3, t);	\
	    }	
	    CREATESHIFT(SRL)
	    CREATESHIFT(SRA)
	    CREATESHIFT(SLL)
#undef CREATESHIFT
*/

#define CREATESHIFT(op)								\
	    static VInst *create##op (RegNum src, RegNum shi, RegNum dst, DataType t)	\
	    {									\
		Operand opr1(Operand::VREG_USE),				\
			opr2(Operand::VREG_USE),				\
			opr3(Operand::VREG_DEF);				\
		opr1.reg = src;							\
		opr2.reg = shi;							\
		opr3.reg = dst;							\
		return new VInst3 (OP_##op, OPK_COMPUTATION, opr1, opr2, opr3, t);	\
	    }	

	    CREATESHIFT(SRL)
	    CREATESHIFT(SRA)
	    CREATESHIFT(SLL)

#undef CREATESHIFT

	private:
	    Operand operand[3];
    };

    // VInst with FOUR operands
    class VInst4 : public VInst {
	public:
		VInst4()
			: VInst() {}
	   	    
	    // Attention: only valid when i >= 1 && i <= 4, otherwise, undefined
	    Operand getOperand(int i) const
	    {
		return operand[i - 1];
	    }

	    void setOperand(int i, Operand oprnd)
	    {
		operand[i - 1] = oprnd;
	    }
	    
	    int getOperandNum() const { return 4; }

		int getSize()
		{
			return sizeof(VInst4);
		}

        /*
	    // src: source float-point register number
	    // srcsize: source data size
	    // dst: destination float-point register number
	    // dstsize: destination data size
	    static VInst * createFCONV(RegNum src, BlockSize srcsize, RegNum dst, BlockSize dstsize)
	    {
		Operand opr1(Operand::VREG_USE),
			opr2(Operand::SIZE),
			opr3(Operand::VREG_USE), 
			opr4(Operand::SIZE);

		opr1.reg = src;
		opr2.size = srcsize;
		opr3.reg = dst;
		opr4.size = dstsize;
		return new VInst4(OP_FCONV, OPK_SPECIAL, opr1, opr2, opr3, opr4);
	    }
        */


	    // cc: compare condition code
	    // r1: v-register number, left operand
	    // r2: v-register number, right operand
	    // r3: v-register number, where compare result (true/false) is stored
	    static VInst * createCMP(CmpCC cc, RegNum r1, RegNum r2, RegNum r3)
	    {
		Operand opr1(Operand::CC),    
			opr2(Operand::VREG_USE),    
			opr3(Operand::VREG_USE),
			opr4(Operand::VREG_DEF); 

		opr1.cc = cc;
		opr2.reg = r1;
		opr3.reg = r2;
		opr4.reg = r3;
		return new VInst4(OP_CMP, OPK_COMPUTATION, opr1, opr2, opr3, opr4);
	    }

	    // cc: compare condition code
	    // r1: float-point register number, left operand
	    // r2: float-point register number, right operand
	    // r3: float-point register number, where compare result (true/false) is stored
	    static VInst * createFCMPS(CmpCC cc, RegNum r1, RegNum r2, RegNum r3)
	    {
		Operand opr1(Operand::CC),    
			opr2(Operand::VREG_USE),    
			opr3(Operand::VREG_USE),
			opr4(Operand::FREG_DEF); 

		opr1.cc = cc;
		opr2.reg = r1;
		opr3.reg = r2;
		opr4.reg = r3;
		return new VInst4(OP_FCMP_S, OPK_COMPUTATION, opr1, opr2, opr3, opr4);
	    }

	    // cc: compare condition code
	    // r1: float-point register number, left operand
	    // r2: float-point register number, right operand
	    // r3: float-point register number, where compare result (true/false) is stored
	    static VInst * createFCMPD(CmpCC cc, RegNum r1, RegNum r2, RegNum r3)
	    {
		Operand opr1(Operand::CC),    
			opr2(Operand::VREG_USE),    
			opr3(Operand::VREG_USE),
			opr4(Operand::FREG_DEF); 

		opr1.cc = cc;
		opr2.reg = r1;
		opr3.reg = r2;
		opr4.reg = r3;
		return new VInst4(OP_FCMP_D, OPK_COMPUTATION, opr1, opr2, opr3, opr4);
	    }

	protected:
	    VInst4 (Opcode op, Opkind kind, 
		    Operand opr1, Operand opr2, Operand opr3, Operand opr4, DataType t = notype)
		: VInst(op, kind, t)
	    {
            operand[0] = opr1;
            operand[1] = opr2;
            operand[2] = opr3;
            operand[3] = opr4;
            for( int i = 0 ; i < 4 ; i++ ) {
                if( (operand[i].type == Operand::VREG_USE || operand[i].type == Operand::VREG_DEF) && operand[i].reg >= 15 && operand[i].reg <= 18 ){
                    this -> setUseFlags( true );
                }
            }
        }

	private:
	    Operand operand[4];
    };

    // VInst with FIVE operand
    class VInst5 : public VInst {
	public:
		VInst5()
			: VInst() {}
	    // Attention: only valide when i == 1, otherwise, undefined.
	    Operand getOperand(int i) const
	    {
		return operand[i - 1];
	    }

	    void setOperand(int i, Operand oprnd)
	    {
		operand[i - 1] = oprnd;
	    }

	    int getOperandNum() const { return 5; }

		int getSize()
		{
			return sizeof(VInst5);
		}

        // modified by zhangyichao, 2009-11-26
        // to MUL & DIV, r3 is ommitted.
#define CREATECOMP5(op)	\
	    static VInst * create##op(RegNum r1, RegNum r2, RegNum r3, RegNum r4, RegNum r5, DataType t)	\
	    {	    \
		Operand opr1(Operand::VREG_USE),    \
			opr2(Operand::VREG_USE),    \
			opr3(Operand::VREG_USE),    \
			opr4(Operand::VREG_DEF),    \
			opr5(Operand::VREG_DEF);    \
		opr1.reg = r1;			\
		opr2.reg = r2;			\
		opr3.reg = r3;			\
		opr4.reg = r4;			\
		opr5.reg = r5;			\
		return new VInst5(OP_##op, OPK_COMPUTATION, opr1, opr2, opr3, opr4, opr5, t);	\
	    }
	    //CREATECOMP5(MUL)
	    //CREATECOMP4(MULU)
	    CREATECOMP5(DIV)
	    //CREATECOMP4(DIVU)
#undef CREATECOMP4
        
        static VInst * createMUL(RegNum r1, RegNum r2, RegNum r3, RegNum r4, RegNum r5, DataType t)
        {
		Operand opr1(Operand::VREG_USE),
			opr2(Operand::VREG_USE),
			opr3(Operand::VREG_USE),
			opr4(Operand::VREG_DEF),
			opr5(Operand::VREG_DEF);

        if(t == f64)
        {
            opr1.type = Operand::FREG_USE;
            opr2.type = Operand::FREG_USE;
            opr3.type = Operand::FREG_USE;
            opr4.type = Operand::FREG_DEF;
            opr5.type = Operand::FREG_DEF;
        }

		opr1.reg = r1;
		opr2.reg = r2;
		opr3.reg = r3;
		opr4.reg = r4;
		opr5.reg = r5;
		return new VInst5(OP_MUL, OPK_COMPUTATION, opr1, opr2, opr3, opr4, opr5, t);
        }

	    static VInst * createBRANCH(CmpCC cc, RegNum r1, RegNum r2, 
					RegNum reg, Word disp)
	    {
		Operand opr1(Operand::CC),
			opr2(Operand::VREG_USE),
			opr3(Operand::VREG_USE),
			opr4(Operand::VREG_USE),
			opr5(Operand::IMM);

		opr1.cc = cc;
		opr2.reg = r1;
		opr3.reg = r2;
		opr4.reg = reg;
		opr5.imm = disp;
		return new VInst5(OP_BRANCH, OPK_CONTROLTRANSFER, 
			          opr1, opr2, opr3, opr4, opr5);
	    }
	    
        /*
	    static VInst * createDIVU64(RegNum r1, RegNum r2, RegNum r3, 
					RegNum r4, RegNum r5)
	    {
		Operand opr1(Operand::VREG_USE),
			opr2(Operand::VREG_USE),
			opr3(Operand::VREG_USE),
			opr4(Operand::VREG_DEF),
			opr5(Operand::VREG_DEF);

		opr1.reg = r1;
		opr2.reg = r2;
		opr3.reg = r3;
		opr4.reg = r4;
		opr5.reg = r5;
		return new VInst5(OP_DIVU64, OPK_COMPUTATION, 
			          opr1, opr2, opr3, opr4, opr5);
	    }
        */
	protected:
	    VInst5(Opcode op, Opkind kind, 
		   Operand opr1, Operand opr2, Operand opr3, Operand opr4, Operand opr5, DataType t = notype) 
		:VInst(op, kind, t)
	    {
            operand[0] = opr1;
            operand[1] = opr2;
            operand[2] = opr3;
            operand[3] = opr4;
            operand[4] = opr5;
            for( int i = 0 ; i < 5 ; i++ ) {
                if( (operand[i].type == Operand::VREG_USE || operand[i].type == Operand::VREG_DEF) && operand[i].reg >= 15 && operand[i].reg <= 18 ){
                    this -> setUseFlags( true );
                }
            }
        }

	private:
	    Operand operand[5];
    };


    class VInstVar : public VInst {
	public:
		VInstVar ()
			: VInst() {}

	    Operand getOperand(int i) const
	    {
		return operand[i - 1];
	    }

	    void setOperand(int i, Operand oprnd)
	    {
		operand[i - 1] = oprnd;
	    }

	    int getOperandNum() const
	    {
		return operand.size();
	    }

		int getSize()
		{
			return sizeof(VInstVar);
		}

	    static VInst * createCALL (Word disp, int n, ...)
	    {
		VInstVar *inst = new VInstVar(OP_CALL, OPK_SPECIAL);		

		Operand func(Operand::WORD), 
			argN(Operand::WORD), 
			arg(Operand::VREG_USE);

		func.word = disp;
		argN.word = n;

		inst->operand.push_back(func);
		inst->operand.push_back(argN);

		// push all the function arguments
		va_list ap;
		va_start(ap, n);
		for (int i = 0; i < n; i++) {
		    arg.reg = (short)va_arg(ap, Word);
		    inst->operand.push_back(arg);
		}
		va_end(ap);

		return inst;
	    }

	protected:
	    VInstVar (Opcode op, Opkind kind)
		: VInst(op, kind)
		{
		}
	
	private:
	    std::vector<Operand> operand;
    };

}

#endif
